﻿' ----------------------------------------------------------------------------------
' Microsoft Developer & Platform Evangelism
' 
' Copyright (c) Microsoft Corporation. All rights reserved.
' 
' THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
' EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES 
' OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ----------------------------------------------------------------------------------
' The example companies, organizations, products, domain names,
' e-mail addresses, logos, people, places, and events depicted
' herein are fictitious.  No association with any real company,
' organization, product, domain name, email address, logo, person,
' places, or events is intended or should be inferred.
' ----------------------------------------------------------------------------------

Imports Microsoft.Samples.WindowsPhoneCloud.Web.Controllers
Imports Microsoft.Samples.WindowsPhoneCloud.Web.Infrastructure
Imports Microsoft.Samples.WindowsPhoneCloud.Web.Models
Imports Microsoft.Samples.WindowsPhoneCloud.Web.Services
Imports Microsoft.WindowsAzure
Imports Microsoft.WindowsAzure.ServiceRuntime
Imports Microsoft.WindowsAzure.StorageClient
Imports System.Globalization
Imports System.Data.Entity

Public Class MvcApplication
	Inherits System.Web.HttpApplication
	Private Const DefaultHttpsPort As Integer = 443
	Private Const DefaultHttpPort As Integer = 10080
	Private Const PortErrorMessage As String = "The Web role was started in a wrong port." & ControlChars.CrLf & "											For this sample application to work correctly, please make sure that it is running in port {0}. " & ControlChars.CrLf & "											Please review the Troubleshooting section of the sample documentation for instructions on how to do this."

	Private Shared securityInitialized As Boolean = False

	Public Shared Sub RegisterGlobalFilters(ByVal filters As GlobalFilterCollection)
		filters.Add(New HandleErrorAttribute())
	End Sub

	Public Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
		routes.IgnoreRoute("{resource}.axd/{*pathInfo}")

		routes.MapRoute("Default", "{controller}/{action}/{id}", New With {Key .controller = "Home", Key .action = "Index", Key .id = CStr(Nothing)}, New With {Key .controller = New ListConstraint(ListConstraintType.Exclude, "RegistrationService", "SharedAccessSignatureService", "PushNotificationService", "SqlAzureSampleODataService")})
	End Sub

	Protected Sub Application_Start()
		' This code sets up a handler to update CloudStorageAccount instances when their corresponding
		' configuration settings change in the service configuration file.
		' Provide the configSetter with the initial value.
		CloudStorageAccount.SetConfigurationSettingPublisher(Sub(configName, configSetter) configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)))

        AreaRegistration.RegisterAllAreas()
        RegisterGlobalFilters(GlobalFilters.Filters)
		RegisterRoutes(RouteTable.Routes)

		RouteTable.Routes.AddWcfServiceRoute(Of RegistrationService)("RegistrationService")
		RouteTable.Routes.AddWcfServiceRoute(Of SharedAccessSignatureService)("SharedAccessSignatureService")
		RouteTable.Routes.AddWcfServiceRoute(Of SamplePushUserRegistrationService)("PushNotificationService")
		RouteTable.Routes.AddWcfServiceRoute(Of SqlAzureSampleODataService)("SqlAzureSampleODataService")

        Dim account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString")
        CreateSilverlightClientAccessPolicy(account.CreateCloudBlobClient())
        CreateCloudTables(account.CreateCloudTableClient())

        Database.SetInitializer(Of SqlSampleDataContext)(New SqlSampleDataInitializer())
	End Sub

	Protected Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
		If (Not securityInitialized) Then
			InitializeSecurity()
			securityInitialized = True
		End If
	End Sub

    Private Shared Sub CreateSilverlightClientAccessPolicy(ByVal cloudBlobClient As CloudBlobClient)
        Dim container = cloudBlobClient.GetContainerReference("$root")
        container.CreateIfNotExist()
        container.SetPermissions(New BlobContainerPermissions With {.PublicAccess = BlobContainerPublicAccessType.Blob})

        Dim blob = cloudBlobClient.GetBlobReference("clientaccesspolicy.xml")
        blob.Properties.ContentType = "text/xml"
        blob.UploadText("<?xml version=""1.0"" encoding=""utf-8""?>" & ControlChars.CrLf & _
                "                <access-policy>" & ControlChars.CrLf & _
                "                  <cross-domain-access>" & ControlChars.CrLf & _
                "                    <policy>" & ControlChars.CrLf & _
                "                      <allow-from http-methods=""*"" http-request-headers=""*"">" & ControlChars.CrLf & _
                "                        <domain uri=""*"" />" & ControlChars.CrLf & _
                "                        <domain uri=""http://*"" />" & ControlChars.CrLf & _
                "                      </allow-from>" & ControlChars.CrLf & _
                "                      <grant-to>" & ControlChars.CrLf & _
                "                        <resource path=""/"" include-subpaths=""true"" />" & ControlChars.CrLf & _
                "                      </grant-to>" & ControlChars.CrLf & _
                "                    </policy>" & ControlChars.CrLf & _
                "                  </cross-domain-access>" & ControlChars.CrLf & _
                "                </access-policy>")
    End Sub

    Private Shared Sub CreateCloudTables(ByVal cloudTableClient As CloudTableClient)
        CreatePushNotificationTable(cloudTableClient)
        CreateUserTable(cloudTableClient)
        CreateUserPrivilegeTable(cloudTableClient)
    End Sub

    Private Shared Sub CreatePushNotificationTable(ByVal cloudTableClient As CloudTableClient)
        cloudTableClient.CreateTableIfNotExist(UserTablesServiceContext.PushUserTableName)

        ' Execute conditionally for development storage only.
        If cloudTableClient.BaseUri.IsLoopback Then
            Dim context = cloudTableClient.GetDataServiceContext()
            Dim entity = New PushUserEndpoint With {.UserId = "UserName", .ChannelUri = "http://tempuri", .TileCount = 0}

            context.AddObject(UserTablesServiceContext.PushUserTableName, entity)
            context.SaveChangesWithRetries()
            context.DeleteObject(entity)
            context.SaveChangesWithRetries()
        End If
    End Sub

    Private Shared Sub CreateUserTable(ByVal cloudTableClient As CloudTableClient)
        cloudTableClient.CreateTableIfNotExist(UserTablesServiceContext.UserTableName)

        ' Execute conditionally for development storage only.
        If cloudTableClient.BaseUri.IsLoopback Then
            Dim context = cloudTableClient.GetDataServiceContext()
            Dim entity = New User With {.Name = "UserName", .Email = "user@email.com"}

            context.AddObject(UserTablesServiceContext.UserTableName, entity)
            context.SaveChangesWithRetries()
            context.DeleteObject(entity)
            context.SaveChangesWithRetries()
        End If
    End Sub

    Private Shared Sub CreateUserPrivilegeTable(ByVal cloudTableClient As CloudTableClient)
        cloudTableClient.CreateTableIfNotExist(UserTablesServiceContext.UserPrivilegeTableName)

        ' Execute conditionally for development storage only.
        If cloudTableClient.BaseUri.IsLoopback Then
            Dim context = cloudTableClient.GetDataServiceContext()
            Dim entity = New UserPrivilege With {.UserId = "UserId", .Privilege = "Privilege"}

            context.AddObject(UserTablesServiceContext.UserPrivilegeTableName, entity)
            context.SaveChangesWithRetries()
            context.DeleteObject(entity)
            context.SaveChangesWithRetries()
        End If
    End Sub

	Protected Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
		If Me.ShouldRedirectToHttps() Then
			Me.RedirectScheme(Me.Context.Request.Url, "https")
		ElseIf Me.ShouldRedirectToHttp() Then
			Me.RedirectScheme(Me.Context.Request.Url, "http")
		End If

		If (Not Me.IsPortNumberOK()) AndAlso (Not IsAllowedContent(Me.Context.Request.Path)) Then
			Me.CreateWrongPortException()
		End If
	End Sub

	Private Shared Sub InitializeSecurity()
		Dim adminUser = Membership.FindUsersByName("admin").Cast(Of MembershipUser)().FirstOrDefault()
		If adminUser Is Nothing Then
			adminUser = Membership.CreateUser("admin", "Passw0rd!", "admin@wp7cloudapp.com")
		End If

		Dim adminUserId = adminUser.ProviderUserKey.ToString()
		Dim userPrivilegesRepository As IUserPrivilegesRepository = New UserTablesServiceContext()
		userPrivilegesRepository.AddPrivilegeToUser(adminUserId, PrivilegeConstants.AdminPrivilege)
	End Sub

	Private Shared Function IsAllowedContent(ByVal path As String) As Boolean
		Return path.EndsWith("/Error", StringComparison.OrdinalIgnoreCase) OrElse path.StartsWith("/Content", StringComparison.OrdinalIgnoreCase) OrElse path.StartsWith("/Scripts", StringComparison.OrdinalIgnoreCase)
	End Function

	Private Sub RedirectScheme(ByVal originalUri As Uri, ByVal intendedScheme As String)
		Dim portNumber As Integer = 0
		If intendedScheme.Equals("https", StringComparison.OrdinalIgnoreCase) Then
			portNumber = DefaultHttpsPort
		ElseIf intendedScheme.Equals("http", StringComparison.OrdinalIgnoreCase) Then
			portNumber = DefaultHttpPort
		End If

		Dim redirectUrl = String.Format(CultureInfo.InvariantCulture, "{0}://{1}:{2}{3}", intendedScheme, originalUri.Host, portNumber, originalUri.PathAndQuery)

		Me.Response.Redirect(redirectUrl, True)
	End Sub

	Private Function ShouldRedirectToHttp() As Boolean
		Return Me.Request.IsSecureConnection AndAlso Me.Context.Request.Url.ToString().EndsWith(".cer", StringComparison.OrdinalIgnoreCase)
	End Function

	Private Function ShouldRedirectToHttps() As Boolean
		Return (Not Me.Request.IsSecureConnection) AndAlso Not Me.Context.Request.Url.ToString().EndsWith(".cer", StringComparison.OrdinalIgnoreCase)
	End Function

	Private Sub CreateWrongPortException()
		Dim exception = New RoleInWrongPortException(String.Format(CultureInfo.InvariantCulture, PortErrorMessage, DefaultHttpsPort))
		Dim routeData = New RouteData()
		routeData.Values.Add("Controller", "Error")
		routeData.Values.Add("Action", "Index")
		routeData.Values.Add("Error", exception)

		Using errorController = New Microsoft.Samples.WindowsPhoneCloud.Web.Controllers.ErrorController()
			CType(errorController, IController).Execute(New RequestContext(New HttpContextWrapper(Me.Context), routeData))
		End Using

		Me.Context.Response.End()
	End Sub

	Private Function IsPortNumberOK() As Boolean
		Dim scheme = Me.Context.Request.Url.Scheme
		Dim portNumber = 0

		If scheme.Equals("https") Then
			portNumber = DefaultHttpsPort
		ElseIf scheme.Equals("http") Then
			portNumber = DefaultHttpPort
		End If

		Dim hostAddress = If(Me.Context.Request.Headers("Host"), String.Empty)
		Dim portPosition = hostAddress.IndexOf(":", StringComparison.OrdinalIgnoreCase)

		If portPosition > 0 Then
			Integer.TryParse(hostAddress.Substring(portPosition + 1), portNumber)
		End If

		Return (portNumber = DefaultHttpsPort) OrElse ((portNumber = DefaultHttpPort) AndAlso Context.Request.Url.ToString().EndsWith(".cer", StringComparison.OrdinalIgnoreCase))
	End Function
End Class
